#include "memPool_t.h"
#include <iostream>

int memPool_t::PAGE_SIZE = 1024;

memPage_t::memPage_t(int capacity) :

	buffer(new char[capacity]),

	CAPACITY(capacity),

	POSITION(0),

	SIZE(0)
{}

int memPage_t::capacity() const{
	return CAPACITY;
}

bool memPage_t::full() const{
	return (SIZE == capacity());
}

bool memPage_t::empty() const{
	return (SIZE == 0);
}

int memPage_t::size() const{
	return SIZE;
}

int memPage_t::getCurrentPosition() const{
	return POSITION;
}

// returns new PAGE_SIZE if successful, -1 for failure.
int memPage_t::setCurrentPosition(int pos){
	if (pos <= SIZE){
		POSITION = pos;
		return pos;
	}
	else return -1;
}

memPage_t::~memPage_t(){

	delete[] buffer;
}

int memPage_t::write(const char *mem, int size, int pos){

	int bytesWritten = size;
	if (pos > SIZE){ 
		return 0; //holes aren't allowed
	}
	if (pos+size > SIZE){
		SIZE = pos+size;
	}
	if (SIZE > capacity()){
		bytesWritten = capacity()-pos;
		SIZE = capacity();
	}
	
	memcpy(buffer+pos,mem,bytesWritten);
	
	return bytesWritten;
}

int memPage_t::read(char *mem, int size, int pos) const{

	int bytesRead = size;
	if (pos > SIZE){
		return 0; //holes aren't allowed
	}
	if (pos+size>capacity()){
		bytesRead=capacity()-pos;
	}

	memcpy(mem,buffer+pos,bytesRead);

	return bytesRead;
}

int memPage_t::read(char *mem, int size) const
{
	return read(mem,size,POSITION);
}

int memPage_t::write(const char *mem, int size)
{
	return write(mem,size,POSITION);
}


memPool_t::memPool_t(int page_size, int num_pages) 
{
	init(page_size,num_pages);	
}


memPool_t::memPool_t(int page_size) 
{
	init(page_size,1);
}

void memPool_t::init(int page_size, int num_pages) {

	PAGE_SIZE = page_size;

	CAPACITY = 0;

	POSITION = 0;

	SIZE = 0;

	pages = List();

	for (int i = 0; i < num_pages; i++){
		pages.push_back(new memPage_t(page_size));
	}
}

memPool_t::~memPool_t(){
	destroy();
}

void memPool_t::destroy(){
	int num = num_pages();
	for (int i = 0; i < num; i++){
		delete pages.back();
		pages.pop_back();
	}
}

int memPool_t::getCurrentPosition() const{
	return POSITION;
}

// returns new PAGE_SIZE if successful, -1 for failure.
int memPool_t::setCurrentPosition(int pos){
	if (pos <= SIZE){
		POSITION = pos;
		return pos;
	}
	else return -1;
}

int memPool_t::getPageSize() const
{
	return PAGE_SIZE;
}

// returns new PAGE_SIZE if successful, -1 for failure.
int memPool_t::setPageSize(int new_page_size)
{
	if (new_page_size > 0){
		return PAGE_SIZE = new_page_size; 
	}
	else{
		return -1; //PAGE_SIZE must be positive
	}
	
}

int memPool_t::write(const void* addr, int size, int pos){

	if (pos > SIZE){
		return -1; //holes aren't allowed!
	}
	char* mem = (char*)addr;
	
	while (pos+size > CAPACITY){ //allocate required memory
		pages.push_back(new memPage_t(PAGE_SIZE));
		CAPACITY += PAGE_SIZE;
	}
	
	int page_indx=0,page_offset=0,bytesWritten=0,cursor=0;
	memPage_t *page = pages[page_indx];

	//set the cursor, page index and page offset
	while(cursor+page->capacity() <= pos){
		cursor += page->capacity();
		page=pages[++page_indx];
	}

	page_offset = pos-cursor;
	cursor = pos;

	while (cursor < pos+size){

		bytesWritten += page->write(mem+bytesWritten,size-bytesWritten,page_offset);
		cursor=pos+bytesWritten;
		page_offset += bytesWritten;
		if (page_offset >= page->capacity()){
			page=pages[++page_indx];
			page_offset = 0;
		}
	}

	if (pos+size > SIZE) SIZE = pos+size; 
	return bytesWritten;
}

int memPool_t::write(const void* mem, int size)
{
	return write(mem,size,POSITION);
}

int memPool_t::read(void* addr, int size, int pos) const{

	if (pos > SIZE){
		return -1; //holes aren't allowed!
	}
	char* mem = (char*)addr;
	
	int page_indx=0,page_offset,bytesRead=0,cursor=0;
	memPage_t *page = pages[page_indx];

	while(cursor+page->capacity() <= pos){
		cursor += page->capacity();
		page=pages[++page_indx];
	}
	page_offset = pos-cursor;
	cursor = pos;

	while (cursor < pos+size)
	{	
		bytesRead += page->read(mem+bytesRead,size-bytesRead,page_offset);
		cursor = pos+bytesRead;
		page_offset += bytesRead;
		if (page_offset >= page->capacity()){
			page=pages[++page_indx];
			page_offset = 0;
		}
	}

	return bytesRead;
}

int memPool_t::read(void* mem, int size) const
{
	return read(mem,size,POSITION);
}

bool memPool_t::full() const{
	return SIZE==capacity();
}

bool memPool_t::empty() const{
	return SIZE==0;
}

int memPool_t::capacity() const{
	return CAPACITY;
}

int memPool_t::num_pages() const{
	return pages.size();
}

int memPool_t::size() const{
	return SIZE;
}


int main(int argc, char** argv){

	memPool_t newPool(1024);
	double n = 0.5;
	for (int i=0; i < 500; i++){
		n += 1;
		newPool.write(&n,sizeof(double),newPool.size());
	}

	std::cout << newPool.capacity() << "," << newPool.size() << std::endl;
	newPool.setPageSize(19);

	for (int i=500; i < 1000; i++){		
		n += 1;
		newPool.write(&n,sizeof(double),newPool.size());
	}

	std::cout << newPool.getCurrentPosition() << std::endl;
	
	std::cout << newPool.capacity() << "," << newPool.size() << std::endl;
	newPool.setPageSize(2);
	std::cout << newPool.capacity() << "," << newPool.size() << std::endl;

	for (int i=1000; i < 1500; i++){
		n += 1;
		newPool.write(&n,sizeof(double),newPool.size());
	}

	newPool.setPageSize(128);

	for (int i=1500; i < 2000; i++){
		n += 1;
		newPool.write(&n,sizeof(double),newPool.size());
	}

	std::cout << newPool.capacity() << "," << newPool.size() << std::endl;
	std::cout << newPool.getCurrentPosition() << std::endl;

	double j;
	for (int i=0; i < 2000; i++){
		
		newPool.read(&j,sizeof(double),i*sizeof(double));
		std::cout << "j="<< j <<std::endl;
	}
	std::cout << newPool.capacity() << "," << newPool.size() << std::endl;
	std::cout << newPool.getCurrentPosition() << std::endl;
	j=0;

}